/////////////////////////////////////////////////////////////////////////////
// Name:        mp3encoder.cpp
// Purpose:     A wxWidgets wrapper around LAME
// Author:      Julian Smart
// Modified by: 
// Created:     2009-02-10
// RCS-ID:      
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#include "wx/filename.h"
#include "wx/process.h"
#include "wx/txtstrm.h"
#include "wx/textfile.h"
#include "wx/datstrm.h"
#include "wx/wfstream.h"
#include "wx/log.h"

#include "tts/mp3encoder.h"

#include "wx/arrimpl.cpp"

WX_DEFINE_OBJARRAY(wxID3InfoArray)

class wxMP3EncoderProcess: public wxProcess
{
public:
    DECLARE_CLASS(wxMP3EncoderProcess)

    wxMP3EncoderProcess(const wxString& cmd, wxMP3Encoder* encoder, wxEvtHandler* handler): wxProcess(handler, wxPROCESS_REDIRECT), m_cmd(cmd)
    {
        m_eventHandler = handler;
        m_encoder = encoder;
        Redirect();
    }

    virtual void OnTerminate(int pid, int status);

    virtual bool HasInput();

protected:
    wxString        m_cmd;
    wxEvtHandler*   m_eventHandler;
    wxMP3Encoder*   m_encoder;
};

IMPLEMENT_CLASS(wxMP3EncoderProcess, wxProcess)

void wxMP3EncoderProcess::OnTerminate(int pid, int status)
{
    // show the rest of the output
    while ( HasInput() )
        ;

    if (m_encoder)
        m_encoder->SetProcess(NULL);

    // wxFile::Flush can cause system error messages on Mac,
    // but they seem to be harmless, so switch off logging
    // temporarily
    wxLog::EnableLogging(false);
    
    // send termination event
    wxProcess::OnTerminate(pid, status);

    wxLog::EnableLogging(true);
    
    // Deleted by OnTerminate
    // delete this;
}

bool wxMP3EncoderProcess::HasInput()
{
    bool hasInput = false;

    if ( IsInputAvailable() )
    {
        wxTextInputStream tis(*GetInputStream());

        // this assumes that the output is always line buffered
        wxString msg = tis.ReadLine() ;

        if (m_encoder)
            m_encoder->WriteOutput(msg);

        hasInput = true;
    }

    if ( IsErrorAvailable() )
    {
        wxTextInputStream tis(*GetErrorStream());

        // this assumes that the output is always line buffered
        wxString msg = tis.ReadLine() ;

        // Make sure we don't output *** if the line is empty
        wxString err = msg;
        err.Trim(true);
        if (!err.IsEmpty())
        {
            msg = wxT("*** ") + msg;
            msg.Replace(wxT("\n"), wxT("\n*** "));
        }

        if (m_encoder)
            m_encoder->WriteOutput(msg);

        hasInput = true;
    }

    return hasInput;
}

/*
 * wxMP3Encoder implementation
 */

IMPLEMENT_DYNAMIC_CLASS( wxMP3Encoder, wxEvtHandler )

wxMP3Encoder::wxMP3Encoder()
{
    Init();
}

wxMP3Encoder::~wxMP3Encoder()
{
}

// Member initialisation
void wxMP3Encoder::Init()
{
    m_options = 0;
    //m_programLocation = wxT("lame");
    //m_bitRate = -1; // use default
    m_errorCode = 0;
    m_process = NULL;
    m_pid = 0;
}

// Encode the MP3
bool wxMP3Encoder::Encode(const wxString& inputFilename, const wxString& outputFilename, int options)
{
    if (IsEncoding())
    {
        Stop();

        // TODO: something better, or at least time-out and return false.
        while (IsEncoding())
            wxYield();
    }

    m_options = options;
    m_errorCode = 0;
    m_pid = 0;

    wxString cmd = wxT("\"") + m_info.m_encoderPath + wxT("\"");

    // If this isn't passed, the process can hang after a short time
    if (options & wxMP3ENCODER_OPTION_QUIET)
        cmd << wxT(" --quiet");

    if (m_info.m_bitRate != -1)
        cmd << wxT(" -b ") << m_info.m_bitRate;

    if (!m_extraSwitches.IsEmpty())
        cmd << wxT(" ") << m_extraSwitches;

    if (options & wxMP3ENCODER_OPTION_ID3)
    {
        if (!m_id3Info.GetTitle().IsEmpty())
            cmd << wxT(" --tt \"") << m_id3Info.GetTitle() << wxT("\"");
        if (!m_id3Info.GetArtist().IsEmpty())
            cmd << wxT(" --ta \"") << m_id3Info.GetArtist() << wxT("\"");
        if (!m_id3Info.GetAlbum().IsEmpty())
            cmd << wxT(" --tl \"") << m_id3Info.GetAlbum() << wxT("\"");
        if (!m_id3Info.GetYear().IsEmpty())
            cmd << wxT(" --ty \"") << m_id3Info.GetYear() << wxT("\"");
        if (!m_id3Info.GetComment().IsEmpty())
            cmd << wxT(" --tc \"") << m_id3Info.GetComment() << wxT("\"");
        if (!m_id3Info.GetGenre().IsEmpty())
            cmd << wxT(" --tg \"") << m_id3Info.GetGenre() << wxT("\"");
        if (m_id3Info.GetTrackNumber() != -1)
        {
            wxString str;
            str << m_id3Info.GetTrackNumber();
            if (m_id3Info.GetTrackCount() > 0)
                str << wxT("/") << m_id3Info.GetTrackCount();

            cmd << wxT(" --tn \"") << str << wxT("\"");
        }
    }

    cmd << wxT(" \"") << inputFilename << wxT("\" \"") << outputFilename << wxT("\"");

    m_command = cmd;

    m_process = new wxMP3EncoderProcess(cmd, this, this);
    m_pid = wxExecute(cmd, wxEXEC_ASYNC, m_process);

    if ( !m_pid )
    {
        // wxLogError( _("Execution of '%s' failed."), cmd.c_str() );
        m_pid = 0;

        delete m_process;
        m_process = NULL;
        return false;
    }
    else
    {
        return true;
    }
}

// Stop encoding
bool wxMP3Encoder::Stop()
{
    if (IsEncoding() && (m_pid != 0))
        return wxKill(m_pid, wxSIGKILL) == 0;
    else
        return true;
}

// Process any pending input
bool wxMP3Encoder::ProcessInput()
{
    if (m_process)
    {
        while (m_process->HasInput())
            ;
    }
    return true;
}

/*
 * wxID3Info
 */

bool wxID3Info::operator==(const wxID3Info& info) const
{
    return (
    m_title == info.m_title &&
    m_artist == info.m_artist &&
    m_album == info.m_album &&
    m_year == info.m_year &&
    m_comment == info.m_comment &&
    m_genre == info.m_genre &&
    m_trackNumber == info.m_trackNumber &&
    m_trackCount == info.m_trackCount
        );
}

void wxID3Info::Copy(const wxID3Info& info)
{
    m_title = info.m_title;
    m_artist = info.m_artist;
    m_album = info.m_album;
    m_year = info.m_year;
    m_comment = info.m_comment;
    m_genre = info.m_genre;
    m_trackNumber = info.m_trackNumber;
    m_trackCount = info.m_trackCount;

}

void wxID3Info::Init()
{
    // By default, don't use track number or count
    m_trackNumber = -1;
    m_trackCount = -1;
}

